package com.jhxy.algorithm.majorityelement;

/**
 * Date: 2024/3/11 15:40
 * Author: T_log
 * Description: 多数元素_分治法
 * <p>
 * 思路
 * <p>
 * 如果数 a 是数组 nums 的众数，如果我们将 nums 分成两部分，那么 a 必定是至少一部分的众数。
 * 我们可以使用反证法来证明这个结论。假设 a 既不是左半部分的众数，也不是右半部分的众数，那么 a 出现的次数少于 l / 2 + r / 2 次，其中 l 和 r 分别是左半部分和右半部分的长度。
 * 由于 l / 2 + r / 2 <= (l + r) / 2，说明 a 也不是数组 nums 的众数，因此出现了矛盾。所以这个结论是正确的。
 * 这样以来，我们就可以使用分治法解决这个问题：将数组分成左右两部分，分别求出左半部分的众数 a1 以及右半部分的众数 a2，随后在 a1 和 a2 中选出正确的众数。
 * 算法
 * 我们使用经典的分治算法递归求解，直到所有的子问题都是长度为 1 的数组。长度为 1 的子数组中唯一的数显然是众数，直接返回即可。如果回溯后某区间的长度大于 1，
 * 我们必须将左右子区间的值合并。如果它们的众数相同，那么显然这一段区间的众数是它们相同的值。否则，我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。
 * </p>
 */
public class DivideAndConquer {


    public int majorityElement(int[] nums) {
        return majorityElementRec(nums, 0, nums.length - 1);
    }

    private int majorityElementRec(int[] nums, int left, int right) {
        if (left == right) {
            return nums[left];
        }

        int mid = (right - left) / 2 + left;
        int leftNumber = majorityElementRec(nums, left, mid);
        int rightNumber = majorityElementRec(nums, mid + 1, right);

        if (leftNumber == rightNumber) {
            return leftNumber;
        }

        //确定左边的leftNumber和右边的rightNumber哪个是真正的众数
        int leftCount = countInRange(nums, leftNumber, left, right);
        int rightCount = countInRange(nums, rightNumber, left, right);

        return leftCount > rightCount ? leftNumber : rightNumber;
    }

    private int countInRange(int[] nums, int num, int left, int right) {
        int count = 0;
        for (int i = left; i <= right; i++) {
            if (nums[i] == num) {
                count++;
            }
        }

        return count;
    }
}
